package io.netty.client.core;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.client.server.connectScheduledTask;
import io.netty.util.concurrent.DefaultThreadFactory;
import lombok.extern.slf4j.Slf4j;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
public abstract class AbstractNettyClient implements Runnable {

    private String connServerIp;
    private int connServerPort;

    private final Map<ChannelHandlerBuilder, String> handlerPipeline = new LinkedHashMap<>();

    private Channel channel;

    public AbstractNettyClient() {
    }

    public AbstractNettyClient(String connServerIp, int connServerPort) {
        this.connServerIp = connServerIp;
        this.connServerPort = connServerPort;
    }

    @Override
    public void run() {
        final EventLoopGroup group = new NioEventLoopGroup(1, new DefaultThreadFactory("client-worker"));

        final Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.TCP_NODELAY, true) //无延迟对外发送数据
                .handler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {

                        if (!handlerPipeline.isEmpty()) {
                            ChannelPipeline pipeline = ch.pipeline();
                            for (Map.Entry<ChannelHandlerBuilder, String> entry : handlerPipeline.entrySet()) {
                                if (entry.getValue() == null) {
                                    pipeline.addLast(entry.getKey().build());
                                } else {
                                    pipeline.addLast(entry.getValue(), entry.getKey().build());
                                }
                            }
                        }
                    }
                });
        try {
            do {
                try {
                    log.info("客户端准备连接服务端！连接地址：[{}:{}].", connServerIp, connServerPort);
                    ChannelFuture channelFuture = bootstrap.connect(connServerIp, connServerPort).sync();
                    log.info("客户端连接服务端成功！连接地址：[{}:{}].", connServerIp, connServerPort);
                    this.channel = channelFuture.channel();
                    channel.closeFuture().sync();
                } catch (Exception e) {
                    log.error("客户端链路异常断开！", e);
                }
                try {
                    TimeUnit.MILLISECONDS.sleep(3000);
                } catch (InterruptedException e) {
                    //NOOP
                }
                log.info("客户端链路进行重连...");
            } while (true); //链路断开重试直到成功
        } finally {
            group.shutdownGracefully();
        }
    }

    public void setConnServerIp(String connServerIp) {
        this.connServerIp = connServerIp;
    }

    public void setConnServerPort(int connServerPort) {
        this.connServerPort = connServerPort;
    }

    public AbstractNettyClient addLast(ChannelHandlerBuilder handler) {
        handlerPipeline.put(handler, null);
        return this;
    }

    public AbstractNettyClient addLast(String name, ChannelHandlerBuilder handler) {
        handlerPipeline.put(handler, name);
        return this;
    }

    @FunctionalInterface
    interface ChannelHandlerBuilder {
        ChannelHandler build();
    }

}
